package cn.orangeframe.neon.infrastructure.convertor;

import cn.orangeframe.neon.domain.entity.WorkflowDef;
import cn.orangeframe.neon.domain.entity.WorkflowTask;
import cn.orangeframe.neon.domain.enums.TaskType;
import cn.orangeframe.neon.domain.entity.graph.Cell;
import cn.orangeframe.neon.domain.entity.GraphData;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * GraphToWorkflow 类用于将 GraphData 转换为 WorkflowDef。
 * @author fengpb
 */
public class GraphToWorkflow {

    /**
     * 将 GraphData 转换为 WorkflowDef。
     * @param graphData GraphData 对象
     * @return WorkflowDef 对象
     */
    public static WorkflowDef convert(GraphData graphData) {
        WorkflowDef.WorkflowDefBuilder builder = WorkflowDef.builder();
        List<Cell> cells = graphData.getCells();
        if (CollectionUtils.isEmpty(cells)) {
            throw new RuntimeException("流程图不能为空");
        }
        Map<String, Cell> nodes = cells.stream()
                .filter(cell -> "custom-vue-node".equals(cell.getShape()) || "do-while-vue-node".equals(cell.getShape()))
                .collect(Collectors.toMap(Cell::getId, cell -> cell));
        List<Cell> edges = cells.stream()
                .filter(cell -> "dag-edge".equals(cell.getShape()) || "do-while-edge".equals(cell.getShape()))
                .toList();

        // 节点ID与所有连线Map
        Map<String, List<Cell>> childrenEdgeMap = new HashMap<>();
        // 节点ID与所有子节点Map
        Map<String, List<Cell>> childrenNodeMap = new HashMap<>();
        // 节点ID与所有父节点Map
        Map<String, List<Cell>> parentNodeMap = new HashMap<>();
        for (Cell edge : edges) {
            String source = edge.getSource().getCell();
            List<Cell> edgeCells = childrenEdgeMap.get(source);
            if (edgeCells == null) {
                edgeCells = new ArrayList<>();
            }
            edgeCells.add(edge);
            childrenEdgeMap.put(source, edgeCells);
            List<Cell> nodeCells = childrenNodeMap.get(source);
            if (nodeCells == null) {
                nodeCells = new ArrayList<>();
            }
            nodeCells.add(nodes.get(edge.getTarget().getCell()));
            childrenNodeMap.put(source, nodeCells);

            String target = edge.getTarget().getCell();
            List<Cell> parentCells = parentNodeMap.get(target);
            if (parentCells == null) {
                parentCells = new ArrayList<>();
            }
            parentCells.add(nodes.get(edge.getSource().getCell()));
            parentNodeMap.put(target, parentCells);
        }

        Optional<Cell> start = nodes.values().stream().filter(node -> node.getData() != null && "start".equals(node.getData().getNodeType())).findFirst();
        if (start.isEmpty()) {
            throw new RuntimeException("开始节点不能为空");
        }

        Cell startNode = start.get();
        List<WorkflowTask> tasks = new ArrayList<>();
        builder.name(startNode.getData().getWorkflowName());
        builder.version(startNode.getData().getVersion());
        builder.tasks(tasks);

        // 取开始任务后的第一个任务起始
        tasks.addAll(convertNode(childrenNodeMap.get(startNode.getId()).getFirst(), nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap));
        return builder.build();
    }

    /**
     * 构建节点任务
     * @param node 节点
     * @param nodes 所有节点
     * @param childrenEdgeMap 节点ID与所有连线Map
     * @param childrenNodeMap 节点ID与所有子节点Map
     * @param parentNodeMap 节点ID与所有父节点Map
     * @param isLoop 是否为递归循环任务列表
     * @return 任务列表
     */
    private static List<WorkflowTask> convertNode(Cell node, Map<String, Cell> nodes
            , Map<String, List<Cell>> childrenEdgeMap
            , Map<String, List<Cell>> childrenNodeMap
            , Map<String, List<Cell>> parentNodeMap
            , boolean... isLoop) {
        if (StringUtils.isEmpty(node.getData().getTaskReferenceName())) {
            throw new RuntimeException(String.format("节点[%s]没有任务名称", node.getData().getLabel()));
        }
        List<WorkflowTask> tasks = new ArrayList<>();
        String sourceId = node.getId();
        List<Cell> parentNodes = parentNodeMap.get(sourceId);
        WorkflowTask task = new WorkflowTask();
        if (!TaskType.JOIN.name().equals(node.getData().getNodeType())
                && parentNodes != null && parentNodes.size() > 1
                && (isLoop.length == 0 || !isLoop[0])) {
            // 1.当前节点不为JOIN任务节点
            // 2.当前节点不止有一个父节点
            // 3.不为递归循环任务列表
            // 则代表当前节点为某个循环任务的起点
            Optional<Cell> doWholeNodeOptional = parentNodes.stream().filter(t -> TaskType.DO_WHILE.name().equals(t.getData().getNodeType())).findAny();
            if (doWholeNodeOptional.isPresent()) {
                Cell doWhileNode = doWholeNodeOptional.get();
                WorkflowTask doWhileTask = new WorkflowTask();
                doWhileTask.setTaskReferenceName(doWhileNode.getData().getTaskReferenceName());
                doWhileTask.setType(doWhileNode.getData().getNodeType());
                doWhileTask.setDescription(doWhileNode.getData().getDescription());
                doWhileTask.setInputParameters(JSON.parseObject(doWhileNode.getData().getInputParameters(), new TypeReference<>() {
                }));
                doWhileTask.setExpression(doWhileNode.getData().getExpression());
                doWhileTask.setEvaluatorType(doWhileNode.getData().getEvaluatorType());
                doWhileTask.setLoopCondition(doWhileNode.getData().getLoopCondition());

                // 获取DO_WHILE节点，归调用convertNode方法，将DO_WHILE节点的子节点转换为任务列表
                List<WorkflowTask> loopTasks = convertNode(node, nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap, true);
                doWhileTask.setLoopOver(loopTasks);
                doWhileTask.setLoopCondition(doWhileNode.getData().getLoopCondition());
                tasks.add(doWhileTask);
                // 将当前节点设置为DO_WHILE节点的非循环子节点
                Optional<Cell> first = childrenEdgeMap.get(doWhileNode.getId()).stream()
                        .filter(t -> "dag-edge".equals(t.getShape())).findFirst();
                if (first.isPresent()) {
                    node = nodes.get(first.get().getTarget().getCell());
                }
            } else {
                throw new RuntimeException(String.format("节点[%s]有多个父节点，但不是DO_WHILE类型", node.getData().getLabel()));
            }
        }
        task.setTaskReferenceName(node.getData().getTaskReferenceName());
        task.setType(node.getData().getNodeType());
        task.setDescription(node.getData().getDescription());
        task.setInputParameters(JSON.parseObject(node.getData().getInputParameters(), new TypeReference<>() {
        }));
        task.setExpression(node.getData().getExpression());
        task.setEvaluatorType(node.getData().getEvaluatorType());
        tasks.add(task);

        switch (node.getData().getNodeType()) {
            case TaskType.TASK_TYPE_SWITCH:
                buildSwitch(sourceId, node, task, nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap);
                break;
            case TaskType.TASK_TYPE_HTTP:
            case TaskType.TASK_TYPE_GROOVY:
                buildNormal(sourceId, node, task, tasks, nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap);
                break;
            case TaskType.TASK_TYPE_FORK:
                buildForkJoin(sourceId, node, task, tasks, nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap);
                break;
            case TaskType.TASK_TYPE_DO_WHILE:
                // 当递归到DO_WHILE节点时，不添加到任务列表中
                tasks.remove(task);
                break;
            case TaskType.TASK_TYPE_JOIN:
            case TaskType.TASK_TYPE_TERMINATE:
            default:
                break;
        }
        return tasks;
    }

    // 构造Switch任务
    private static void buildSwitch(String sourceId, Cell node, WorkflowTask task, Map<String, Cell> nodes
            , Map<String, List<Cell>> childrenEdgeMap
            , Map<String, List<Cell>> childrenNodeMap
            , Map<String, List<Cell>> parentNodeMap)  {
        List<Cell> switchEdges = childrenEdgeMap.get(sourceId);
        if (CollectionUtils.isEmpty(switchEdges)) {
            throw new RuntimeException(String.format("Switch节点[%s]没有后继节点", node.getData().getLabel()));
        }
        Optional<Cell> emptyLabel = switchEdges.stream().filter(edge -> CollectionUtils.isEmpty(edge.getLabels())).findAny();
        if (emptyLabel.isPresent()) {
            throw new RuntimeException(String.format("Switch节点[%s]没有分支标签", node.getData().getLabel()));
        }
        // 判断switch节点后所有分支的标签是否重复
        Set<String> labels = switchEdges.stream().map(edge -> edge.getLabels().getFirst().getAttrs().getLabel().getText()).collect(Collectors.toSet());
        if (labels.size() != switchEdges.size()) {
            throw new RuntimeException(String.format("Switch节点[%s]分支标签重复", node.getData().getLabel()));
        }
        Optional<Cell> defaultLabel = switchEdges.stream().filter(edge -> "default".equals(edge.getLabels().getFirst().getAttrs().getLabel().getText()))
                .findAny();
        if (defaultLabel.isEmpty()) {
            throw new RuntimeException(String.format("Switch节点[%s]没有默认节点", node.getData().getLabel()));
        }
        Map<String, List<WorkflowTask>> decisionCases = new HashMap<>();
        List<WorkflowTask> defaultCases = new ArrayList<>();
        task.setDecisionCases(decisionCases);
        task.setDefaultCase(defaultCases);
        for (Cell switchEdge : switchEdges) {
            String caseKey = switchEdge.getLabels().getFirst().getAttrs().getLabel().getText();
            if ("default".equals(caseKey)) {
                List<WorkflowTask> defaultTasks = convertNode(nodes.get(switchEdge.getTarget().getCell()), nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap);
                defaultCases.addAll(defaultTasks);
            } else {
                List<WorkflowTask> caseTasks = convertNode(nodes.get(switchEdge.getTarget().getCell()), nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap);
                decisionCases.put(caseKey, caseTasks);
            }
        }
    }

    // 构造普通任务
    private static void buildNormal(String sourceId, Cell node, WorkflowTask task, List<WorkflowTask> tasks, Map<String, Cell> nodes
            , Map<String, List<Cell>> childrenEdgeMap
            , Map<String, List<Cell>> childrenNodeMap
            , Map<String, List<Cell>> parentNodeMap)  {
        List<Cell> nextEdges = childrenEdgeMap.get(sourceId);
        if (CollectionUtils.isEmpty(nextEdges)) {
            throw new RuntimeException(String.format("节点[%s]没有后继节点", node.getData().getLabel()));
        }
        if (nextEdges.size() > 1) {
            throw new RuntimeException(String.format("节点[%s]只能有一个后继节点", node.getData().getLabel()));
        }
        Cell nextNode = childrenNodeMap.get(sourceId).getFirst();
        List<WorkflowTask> next = convertNode(nextNode, nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap);
        tasks.addAll(next);
    }

    // 构造分支合并任务
    private static void buildForkJoin(String sourceId, Cell node, WorkflowTask task, List<WorkflowTask> tasks
            , Map<String, Cell> nodes
            , Map<String, List<Cell>> childrenEdgeMap
            , Map<String, List<Cell>> childrenNodeMap
            , Map<String, List<Cell>> parentNodeMap) {
        List<Cell> forkEdges = childrenEdgeMap.get(sourceId);
        if (CollectionUtils.isEmpty(forkEdges)) {
            throw new RuntimeException(String.format("节点[%s]没有后继节点", node.getData().getLabel()));
        }
        if (forkEdges.size() == 1) {
            throw new RuntimeException(String.format("节点[%s]至少要有两个并行节点", node.getData().getLabel()));
        }
        List<Cell> forkNodes = childrenNodeMap.get(sourceId);
        List<List<WorkflowTask>> forkTasks = new ArrayList<>(forkEdges.size());
        List<WorkflowTask> joinTasks = new ArrayList<>(forkEdges.size());
        List<String> joinOn = new ArrayList<>(forkEdges.size());
        for (Cell forkNode : forkNodes) {
            List<WorkflowTask> fork = convertNode(forkNode, nodes, childrenEdgeMap, childrenNodeMap, parentNodeMap);
            if (fork.size() < 2) {
                throw new RuntimeException(String.format("节点[%s]分支除Join节点至少应包含一个节点", node.getData().getLabel()));
            }
            WorkflowTask last = fork.getLast();
            if (!last.getType().equals(TaskType.TASK_TYPE_JOIN)) {
                throw new RuntimeException(String.format("节点[%s]分支的最终节点不是Join节点", node.getData().getLabel()));
            }
            WorkflowTask secondToLast = fork.get(fork.size() - 2);
            joinOn.add(secondToLast.getTaskReferenceName());
            joinTasks.add(last);
            forkTasks.add(fork);
        }
        // 判断joinTasks中是否有不重复的join节点
        Set<WorkflowTask> joinTaskSet = new HashSet<>(joinTasks);
        if (joinTaskSet.size() != 1) {
            throw new RuntimeException(String.format("节点[%s]分支的最终节点没有连接到同一个Join节点", node.getData().getLabel()));
        }
        task.setForkTasks(forkTasks);
        WorkflowTask joinTask = joinTasks.getFirst();
        joinTask.setJoinOn(joinOn);
        tasks.add(joinTask);
    }
}
